import os
import pytest
import requests
import yaml
import logging
import re
from jsonpath import jsonpath

# 配置日志记录
logging.basicConfig(filename='test_log.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

# 存储公共参数，如 token
common_params = {}

# 你提供的 RequestUtils 类
logger = logging.getLogger('request_utils')


class RequestUtils:
    sess = requests.Session()
    pubilc_params = {}

    def send_request(self, **kwargs):
        """接收符合要求的参数，发送对应请求，打印日志"""
        logger.info('正在发送请求...')
        params = kwargs.get("params", {})
        params.update(self.pubilc_params)
        kwargs["params"] = params

        for k, v in kwargs.items():
            logger.info(f'  参数内容为【{k}：{v}】')

        resp = self.sess.request(**kwargs)

        logger.info('收到接口响应')
        logger.info(f'  状态码：{resp.status_code}')
        logger.info(f'  响应头：{resp.headers}')
        # logger.info(f'  响应正文：{resp.text}')

        return resp


def read_yaml_files():
    # 获取 tests/yaml_case 目录下所有以 test 开头的 YAML 文件
    yaml_dir = 'tests/yaml_case'
    if not os.path.exists(yaml_dir):
        logging.error(f"目录 {yaml_dir} 不存在")
        return []
    yaml_files = [os.path.join(yaml_dir, f) for f in os.listdir(yaml_dir) if
                  f.startswith('test') and f.endswith('.yaml')]
    test_cases = []
    # 先处理获取公共参数的用例
    for file in yaml_files:
        with open(file, 'r', encoding='utf-8') as f:
            try:
                test_case = yaml.safe_load(f)
                test_case = replace_common_params_in_case(test_case)
                if validate_test_case(test_case):
                    if test_case.get('is_get_common_params', False):
                        execute_get_common_params(test_case)
            except yaml.YAMLError as e:
                logging.error(f"解析 {file} 时出错: {e}")
    # 再处理其他用例
    for file in yaml_files:
        with open(file, 'r', encoding='utf-8') as f:
            try:
                test_case = yaml.safe_load(f)
                test_case = replace_common_params_in_case(test_case)
                if validate_test_case(test_case) and not test_case.get('is_get_common_params', False):
                    test_cases.append(test_case)
            except yaml.YAMLError as e:
                logging.error(f"解析 {file} 时出错: {e}")
    return test_cases


def validate_test_case(test_case):
    required_keys = ['name', 'request']
    request_keys = ['method', 'url']
    for key in required_keys:
        if key not in test_case:
            logging.error(f"用例 {test_case.get('name', '未命名')} 缺少必要字段: {key}")
            return False
    request_info = test_case['request']
    for key in request_keys:
        if key not in request_info:
            logging.error(f"用例 {test_case.get('name', '未命名')} 的请求信息缺少必要字段: {key}")
            return False
    return True


def execute_get_common_params(test_case):
    global common_params
    request_info = test_case['request']
    method = request_info['method']
    url = request_info['url']
    headers = request_info.get('headers', {})
    body = request_info.get('body', {})
    params = request_info.get('params', {})

    # 替换公共参数
    body = replace_common_params(body)
    headers = replace_common_params(headers)
    params = replace_common_params(params)

    request_utils = RequestUtils()
    request_utils.pubilc_params = common_params

    try:
        response = request_utils.send_request(method=method, url=url, headers=headers, json=body, params=params)
    except Exception as e:
        logging.error(f"用例 {test_case['name']} 请求出错: {e}，请求 URL: {url}")
        return

    extract = test_case.get('extract', {})
    for key, (format_type, expr, index) in extract.items():
        try:
            if format_type == 'json':
                result = jsonpath(response.json(), expr)
                if result:
                    common_params[key] = result[index]
            elif format_type == 'text':
                match = re.search(expr, response.text)
                if match:
                    common_params[key] = match.group(index)
            else:
                logging.error(f"用例 {test_case['name']} 不支持的提取格式: {format_type}")
        except Exception as e:
            logging.error(f"用例 {test_case['name']} 提取 {key} 时出错: {e}")


def replace_common_params(data):
    if isinstance(data, str):
        pattern = r'\${([^}]+)}'

        def replace(match):
            param_name = match.group(1)
            return str(common_params.get(param_name, match.group(0)))

        return re.sub(pattern, replace, data)
    elif isinstance(data, dict):
        new_data = {}
        for key, value in data.items():
            new_data[key] = replace_common_params(value)
        return new_data
    elif isinstance(data, list):
        return [replace_common_params(item) for item in data]
    return data


def replace_common_params_in_case(test_case):
    for key, value in test_case.items():
        test_case[key] = replace_common_params(value)
    return test_case


@pytest.mark.parametrize("test_case", read_yaml_files())
def test_api_from_yaml(test_case):
    case_name = test_case['name']
    request_info = test_case['request']
    method = request_info['method']
    url = request_info['url']
    headers = request_info.get('headers', {})
    body = request_info.get('body', {})
    params = request_info.get('params', {})

    # 替换公共参数
    body = replace_common_params(body)
    headers = replace_common_params(headers)
    params = replace_common_params(params)

    request_utils = RequestUtils()
    request_utils.pubilc_params = common_params

    try:
        response = request_utils.send_request(method=method, url=url, headers=headers, json=body, params=params)
    except Exception as e:
        pytest.fail(f"用例 {case_name} 请求出错: {e}，请求 URL: {url}")

    extract = test_case.get('extract', {})
    captured_values = {}
    for key, (format_type, expr, index) in extract.items():
        try:
            if format_type == 'json':
                result = jsonpath(response.json(), expr)
                if result:
                    captured_values[key] = result[index]
            elif format_type == 'text':
                match = re.search(expr, response.text)
                if match:
                    captured_values[key] = match.group(index)
            else:
                pytest.fail(f"用例 {case_name} 不支持的提取格式: {format_type}")
        except Exception as e:
            pytest.fail(f"用例 {case_name} 提取 {key} 时出错: {e}")

    assertions = test_case.get('assertions', [])
    for assertion in assertions:
        key = assertion['key']
        value = assertion['value']
        if key == 'status_code':
            assert response.status_code == value, f"用例 {case_name} 状态码断言失败，期望: {value}，实际: {response.status_code}"
        else:
            try:
                if isinstance(key, str) and key.startswith('$.'):
                    result = jsonpath(response.json(), key)
                elif isinstance(key, str):
                    result = [getattr(response, key, None)]
                if result and result[0] is not None:
                    assert result[0] == value, f"用例 {case_name} 断言失败，期望: {value}，实际: {result[0]}"
                else:
                    pytest.fail(f"用例 {case_name} 未找到 {key} 对应的参数")
            except ValueError as e:
                pytest.fail(f"用例 {case_name} 响应不是合法的 JSON 格式: {e}")
